// make  mod64 CXXFLAGS+="-lgmp -O0 -g" -W mod64.C

#include <iostream>
#include <stdlib.h>
#include <gmp.h>

#define __STDC_LIMIT_MACROS
#include <stdint.h>

// 2^64 = 18446744073709551616
// 2^32 = 4294967296

uint64_t random64() {
	uint32_t low = rand();
	uint64_t hi  = rand();
	// std::cout << hi << ',' << low << std::endl;
	return (low + (hi<<32));
}

int main()
{
	srand(time(NULL));
	std::cout << " 2^64       = 18446744073709551616 " << std::endl;
	std::cout << " UINT64_MAX = " << UINT64_MAX << std::endl;
	std::cout << " 2^32       = 4294967296 " << std::endl;
	std::cout << " UINT32_MAX = " << UINT32_MAX << std::endl;
	std::cout << " RAND_MAX   = " << RAND_MAX << std::endl;
	std::cout << " ********************************* " << std::endl;
	{
		uint64_t p = random64()  ;
		uint64_t q = random64() ;

		uint64_t pq = p * q ;
		// std::cout << pq << std::endl;

		mpz_t two ;
		mpz_init_set_ui(two,2);
		mpz_t two64 ;
		mpz_init(two64);
		mpz_pow_ui(two64,two,64);
		mpz_t p_mpz;
		mpz_init_set_ui(p_mpz,p);
		mpz_t q_mpz;
		mpz_init_set_ui(q_mpz,q);
		mpz_t pq_mpz;
		mpz_init(pq_mpz);
		mpz_t pq64_mpz;
		mpz_init(pq64_mpz);
		mpz_mul(pq_mpz,p_mpz,q_mpz);
		mpz_mod(pq64_mpz,pq_mpz,two64);

		mpz_t pq_uint ;
		mpz_init(pq_uint);
		mpz_init_set_ui(pq_uint,pq);
		if (mpz_cmp(pq_uint,pq64_mpz)) {
			std::cout << "mul mod 2^64 faux" << std::endl << "gmp :";
			mpz_out_str(NULL,10,pq64_mpz);
			std::cout << std::endl << "int :";
			mpz_out_str(NULL,10,pq_uint);
			std::cout << std::endl;
		}
		else {
			std::cout << "mul mod 2^64 ok" << std::endl;
		}

	mpz_clear(p_mpz);
	mpz_clear(q_mpz);
	mpz_clear(pq_mpz);
	mpz_clear(pq_uint);
	mpz_clear(pq64_mpz);
	mpz_clear(two64);
	mpz_clear(two);


	}
	{
		uint64_t p = random64()  ;
		uint64_t q = random64() ;

		uint64_t pq = p + q ;
		if (pq < q  || pq < p)
			--pq ;
		// std::cout << pq << std::endl;

		mpz_t two ;
		mpz_init_set_ui(two,2);
		mpz_t two64 ;
		mpz_init(two64);
		mpz_pow_ui(two64,two,64);
		mpz_t two64m1 ;
		mpz_init(two64m1);
		mpz_sub_ui(two64m1,two64,1);

		mpz_t p_mpz;
		mpz_init_set_ui(p_mpz,p);
		mpz_t q_mpz;
		mpz_init_set_ui(q_mpz,q);
		mpz_t pq_mpz;
		mpz_init(pq_mpz);
		mpz_t pq64_mpz;
		mpz_init(pq64_mpz);
		mpz_add(pq_mpz,p_mpz,q_mpz);
		mpz_mod(pq64_mpz,pq_mpz,two64m1);

		mpz_t pq_uint ;
		mpz_init(pq_uint);
		mpz_init_set_ui(pq_uint,pq);
		if (mpz_cmp(pq_uint,pq64_mpz)) {
			std::cout << "add mod 2^64-1 faux" << std::endl << "gmp :";
			mpz_out_str(NULL,10,pq64_mpz);
			std::cout << std::endl << "int :";
			mpz_out_str(NULL,10,pq_uint);
			std::cout << std::endl;
		}
		else {
			std::cout << "add mod 2^64-1 ok" << std::endl;
		}



	mpz_clear(p_mpz);
	mpz_clear(q_mpz);
	mpz_clear(pq_mpz);
	mpz_clear(pq_uint);
	mpz_clear(pq64_mpz);
	mpz_clear(two64);
	mpz_clear(two64m1);
	mpz_clear(two);

	}
}
